import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import gsap from "gsap";
import { AffectingProperty, PartSetting, StateAspect } from "./type";
import Text from "@/script/Text";
import { boundingCalculator } from "../BoundingCalculator";
import localforage from "localforage";

// 模型状态设置（目前只有初始状态会用到）
type StateAspectSetting = {
  // 状态方面名称
  aspectName: string;
  // 状态值，对于“颜色、位置、旋转”状态方面类型，它是状态值的名称（StateValue.name）
  // 而对于“文字”状态方面类型，它是要显示的文字
  stateValue: string;
};
type rgbToHex = {
  x: number;
  y: number;
  z: number;
};
// 检查状态方面是否是文字类型
function checkAspectText(aspect: StateAspect) {
  return (
    aspect.affectingProperties.length == 1 &&
    aspect.affectingProperties[0] == "TEXT"
  );
}
// Object3D 的元数据
type SceneNodeData = {
  modelId: string; // 模型ID
  modelName: string; // 模型名称
  // name: string // 模型名称，取消了，统一使用 Object3D:name
  code: string; // 模型编号
  specialAttribute: object; // 特有属性
  defaultStateSettings?: StateAspectSetting[]; // 默认状态设置
  isModelGroup?: boolean; // 是否为 ModelGroup（场景内所有模型的根容器）
  isTextGroup?: boolean; // 是否为 TextGroup（物体内多个文字状态的容器）
  isTextObject?: boolean; // 是否为 TextObject（文字对象）
  textObjectStateAspectName?: string; // 此 TextObject 对应的状态方面
  isModel?: boolean; // 是否为模型
  locked?: boolean; // 是否已锁定
  hasStates?: boolean; // 是否有状态
  expanded?: boolean; // 是否已展开（仅用于世界大纲里）
};
type ModelUserData = {
  type: "板卡" | "机柜";
  name: string; //板卡名称
  code: string; // 模型唯一标识
  isModel: boolean; // 是否是模型的根节点
  parentCode: string; // 模型父节点的Code
};
type GlobalDataStructure = {
  cabinets: ModelData[];
};
export type ModelData = {
  modelName: string;
  code: string;
  position: [number, number, number];
  rotation: [number, number, number, string];
  center: THREE.Vector3;
  children: CardMap;
};
export type CardMap = {
  [boardId: number]: {
    code: string;
    position: [number, number, number];
    rotation: [number, number, number, string];
  };
};

const box3 = new THREE.Box3();
const geometry = new THREE.BoxGeometry();
const edges1 = new THREE.EdgesGeometry(geometry);
const boxMaterial1 = new THREE.LineBasicMaterial({
  color: 0xffd700,
  linecap: "butt",
  linejoin: "butt",
  // depthTest: false,
  // transparent: true,
});

// boundingBoxMesh1.material.depthTest = false;
const boundingBoxMesh1 = new THREE.LineSegments(edges1, boxMaterial1);
// 模型解压缩
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("/lib/draco/"); // 设置DRACO解码器的路径
const loader = new GLTFLoader().setDRACOLoader(dracoLoader);

// 模型缓存
let modelCache: { [model: string]: THREE.Object3D } = {};

// 模型状态缓存
let modelStatusCache: { [model: string]: any } = {};

let modelNameArray: { id: string; name: string }[] = []; // id名称对照

// 创建一个最小立方体包围盒-----------------------------------------------------
function highLightTheModel(
  object: THREE.Object3D,
  group: THREE.Group,
  position: [number, number, number]
): THREE.Mesh {
  // 获取模型的边界盒
  const boundingBox = box3.setFromObject(object);
  // 创建包围盒的线条
  const boxMaterial = new THREE.MeshBasicMaterial({
    color: 0xcd5c5c,
    opacity: 0.45,
    transparent: true,
  });
  const boundingBoxMesh = new THREE.Mesh(geometry, boxMaterial);
  boundingBoxMesh.position.set(...position);
  // 设置包围盒的大小
  boundingBox.getSize(boundingBoxMesh.scale).addScalar(0.02);
  // 设置包围盒的位置
  boundingBox.getCenter(boundingBoxMesh.position);
  group.add(boundingBoxMesh);
  // 返回包围盒和模型的引用
  return boundingBoxMesh;
}

// 创建包围线框-----------------------------------------------------------------
function createSurroundWireFrame(mesh: THREE.Object3D, group: THREE.Group) {
  // 创建包围盒的线条
  const boundingBox = box3.setFromObject(mesh);
  // 设置包围盒的大小
  boundingBox.getSize(boundingBoxMesh1.scale).addScalar(0.005);
  // 设置包围盒的位置
  boundingBox.getCenter(boundingBoxMesh1.position);
  group.add(boundingBoxMesh1);
  return boundingBoxMesh1;
}

// 找上层组---------------------------------------------------------------------
function findModelRootNode(child: THREE.Object3D): THREE.Object3D | undefined {
  while (child.parent != null) {
    const parent = child.parent;
    const userData = parent.userData as ModelUserData;
    if (userData.isModel) {
      return parent;
    }
    child = parent;
  }
  return undefined;
}

// 找到卡的模型
function findCardModel(child: THREE.Object3D): THREE.Object3D | undefined {
  while (child.parent != null) {
    const parent = child.parent;
    const userData = parent.userData as ModelUserData;
    if (userData.code) {
      if (userData.code.includes("板卡")) {
        return parent;
      }
    }
    child = parent;
  }
  return undefined;
}

// 控制模型灯显闪烁
function modelFlicker(models: THREE.Object3D[]) {
  // 控制模型闪烁的方法
  console.log("模型闪烁");
  models.forEach((model) => {
    if (Math.random() > 0.7) {
      if (model instanceof THREE.Mesh) {
        model.material = model.material.clone();
        model.material.color.set(0xffff00);
      }
    } else {
      if (model instanceof THREE.Mesh) {
        model.material = model.material.clone();
        model.material.color.setRGB(163 / 255, 163 / 255, 163 / 255);
      }
    }
  });
}

function findGroupByName(
  group: THREE.Group,
  name: string
): THREE.Vector3 | undefined {
  if (group.name.includes(name)) {
    return group.position;
  }

  for (let i = 0; i < group.children.length; i++) {
    const child = group.children[i];
    if (child instanceof THREE.Group) {
      const found = findGroupByName(child, name);
      if (found) {
        return found;
      }
    }
  }

  return undefined;
}

function isObjectOnDoor(child: THREE.Object3D): boolean {
  while (child.parent != null) {
    const parent = child.parent;
    if (parent.name.includes("qianmen")) {
      return true;
    }
    child = parent;
  }
  return false;
}

function findEquipmentCabinetName(child: THREE.Object3D): string {
  while (child.parent != null) {
    const parent = child.parent;
    const userData = parent.userData as ModelUserData;
    if (userData.isModel) {
      return userData.code;
    }
    child = parent;
  }
  return "";
}
// 办卡告警标记
function alarmBoardLabel(
  arr: { id: string; statusValue: string; isError: boolean }[],
  boardCardMap: any,
  originalModelColorMap: {
    [materialId: string]: {
      color: number;
    };
  }
): void {
  arr.forEach((item: { id: string; statusValue: string; isError: boolean }) => {
    if (boardCardMap[item.id]) {
      const model = boardCardMap[item.id].model;
      model.traverse((node: THREE.Object3D) => {
        if (node instanceof THREE.Mesh) {
          const material = node.material as THREE.MeshStandardMaterial;
          originalModelColorMap[material.id] = {
            color: material.color.toJSON(),
          };
          if (node) {
            let color = item.statusValue == "故障" ? 0xff6347 : 0x98fb98;
            node.material.color.set(color);
          }
        }
      });
    }
  });
}

function alarmBoardLabel1(
  item: { itemId: string; itemValue: string; stamp: string },
  group: THREE.Group,
  originalModelColorMap: {
    [materialId: string]: {
      color: number;
    };
  }
): Promise<void> {
  return new Promise((resolve, reject) => {
    group.traverse((object) => {
      const userData = object.userData as ModelUserData;
      if (userData.code == item.itemId) {
        object.traverse((node) => {
          if (node instanceof THREE.Mesh) {
            const material = node.material as THREE.MeshStandardMaterial;
            if (originalModelColorMap[material.id] == null)
              originalModelColorMap[material.id] = {
                color: material.color.toJSON(),
              };
            const originalColor = originalModelColorMap[material.id]?.color;
            if (node) {
              let color =
                item.itemValue == "3"
                  ? 0x40d040
                  : item.itemValue == "2"
                  ? 0xd04040
                  : originalColor;
              node.material.color.set(color);
            }
          }
        });
      }
    });
    resolve();
  });
}

// 恢复颜色
function restoreOriginalColors(
  group: THREE.Group,
  originalModelColorMap: {
    [materialId: string]: {
      color: number;
    };
  }
): void {
  group.traverse((node) => {
    if (node instanceof THREE.Mesh) {
      const material = node.material as THREE.MeshStandardMaterial;
      const originalColor = originalModelColorMap[material.id]?.color;
      if (originalColor !== undefined) {
        material.color.set(originalColor);
      }
    }
  });
}

// 模型加载---------------------------------------------------------------------

// 第一层加载柜体，返回柜子模型对象，记录用于加载以下板卡使用。
function loadModel2(
  model: string,
  code: string,
  parent: THREE.Object3D,
  position: [number, number, number],
  rotation: [number, number, number, string],
  scale: [number, number, number],
  visible: boolean,
  parentCode?: string
): Promise<THREE.Object3D> {
  return new Promise((resolve, reject) => {
    // 如果模型缓存中存在该模型，克隆一份并应用新的信息。
    if (modelCache[model] != null) {
      const isClone = isCloneMaterial(model);
      const modelClone = cloneObject(modelCache[model], isClone);
      groupCloneApplyData(
        model,
        modelClone,
        code,
        parent,
        position,
        rotation,
        scale,
        visible,
        parentCode
      );
      resolve(modelClone);
    } else if (model != "GROUP") {
      // 如果模型缓存中不存在该模型，需要重新加载模型，然后克隆一份并应用新的信息，给加载的模型放到模型缓存中。
      const oldModelUrl = "/models/" + model + ".glb";
      loadWithIndexedDb(oldModelUrl, (newModelUrl) => {
        loader.load(
          newModelUrl,
          function (gltf) {
            const loadedModel = gltf.scene;
            if (loadedModel.children[0].name.includes("GuiTi")) {
              loadedModel.children[0].castShadow = true;
            }
            const group1 = new THREE.Group();
            group1.add(loadedModel);
            modelCache[model] = group1;
            const isClone = isCloneMaterial(model);
            const modelClone = cloneObject(group1, isClone);

            groupCloneApplyData(
              model,
              modelClone,
              code,
              parent,
              position,
              rotation,
              scale,
              visible,
              parentCode
            );
            resolve(modelClone);
          },
          undefined,
          reject
        );
      });
    } else if (model == "GROUP") {
      const group1 = new THREE.Group();
      modelCache[model] = group1;
      const isClone = isCloneMaterial(model);
      const modelClone = cloneObject(group1, isClone);
      groupCloneApplyData(
        model,
        modelClone,
        code,
        parent,
        position,
        rotation,
        scale,
        visible,
        parentCode
      );
      resolve(modelClone);
    }
  });
}

// 克隆模型并应用信息
function groupCloneApplyData(
  model: string,
  modelClone: THREE.Object3D,
  code: string,
  parent: THREE.Object3D,
  position: [number, number, number],
  rotation: [number, number, number, string],
  scale: [number, number, number],
  visible: boolean,
  parentCode?: string
) {
  const userData = modelClone.userData as ModelUserData;
  userData.isModel = true;
  userData.code = code;
  userData.name = model;
  if (parentCode) {
    userData.parentCode = parentCode;
  }
  if (!visible) modelClone.visible = false;

  modelClone.position.set(...position);
  const [x, y, z] = rotation;
  modelClone.rotation.set(x, y, z);
  modelClone.scale.set(...scale);
  parent.add(modelClone);
}

// 用带 IndexedDB 缓存的方式加载二进制文件
// 返回值（无缓存）：原值
// 返回值（有缓存）：URL.createObjectURL(new Blob(arrayBuffer))
function loadWithIndexedDb(url: string, callback: (newUrl: string) => void) {
  localforage.getItem(url, function (error: any, value: any) {
    // console.log('缓存值 of ', url, value);
    if (value == null) {
      // console.log('缓存中没有')
      fetch(url).then((response) => {
        if (response.body) {
          response.arrayBuffer().then((data) => {
            const glbBlob = new Blob([data], { type: "model/gltf-binary" });
            const newUrl = window.URL.createObjectURL(glbBlob);
            localforage.setItem(url, data, function onSaved() {
              callback(newUrl);
            });
          });
        } else {
          // console.log('无法下载数据');
          callback(url);
        }
      });
    } else {
      // console.log('缓存中有', value);
      const glbBlob = new Blob([value as ArrayBuffer], {
        type: "model/gltf-binary",
      });
      const newUrl = window.URL.createObjectURL(glbBlob);
      callback(newUrl);
    }
  });
}

// 返回加载好的模型
function returnLoadModel(
  model: string,
  scene: THREE.Group,
  callback?: (model: THREE.Object3D) => void,
  applyStyle?: (target: THREE.Object3D) => void
) {
  loader.load(
    "/models/" + model + ".glb",
    function (gltf) {
      const loadedModel = gltf.scene;
      const group = new THREE.Group();
      group.add(loadedModel);
      group.name = model;
      if (applyStyle) applyStyle(loadedModel);
      scene.add(group);
      if (callback) callback(group);
    },
    undefined,
    (err) => {
      console.log(err);
    }
  );
}
const matrix = new THREE.Matrix4();

// 实例化加载
function instantiationLoad(
  modelName: string,
  positions: [number, number, number][],
  callback: (model: THREE.Group) => void
) {
  loader.load(
    "/assets/three/models/" + modelName + ".glb",
    function (gltf) {
      const group = new THREE.Group();
      group.name = modelName;
      const loadedModel = gltf.scene;

      loadedModel.traverse((node) => {
        if (node instanceof THREE.Mesh) {
          const geometry = node.geometry;
          const material = node.material as THREE.MeshStandardMaterial;
          const instancedMesh = new THREE.InstancedMesh(
            geometry,
            material,
            positions.length
          );

          for (let i = 0; i < positions.length; i++) {
            matrix.setPosition(...positions[i]);
            instancedMesh.setMatrixAt(i, matrix);
          }

          group.add(instancedMesh);
        }
      });

      callback(group);
    },
    undefined,
    (err) => {
      console.log(err);
    }
  );
}

//接收阴影
function receivingShadow(object: THREE.Object3D) {
  object.traverse((node) => {
    if (node instanceof THREE.Mesh) {
      node.receiveShadow = true; // 接收阴影
    }
  });
}

// 克隆对象（可选不克隆材质）
function cloneObject(object: THREE.Object3D, cloneMaterial = true) {
  const modelClone = object.clone();

  if (cloneMaterial) {
    modelClone.traverse((node) => {
      if (node instanceof THREE.Mesh) {
        node.material = node.material.clone();
      }
    });
  }
  return modelClone;
}

function isCloneMaterial(modelName: string) {
  const whitelist = ["Ditie_XinHaoJi"];
  let isClone;
  const isCard = modelName.includes("BanKa");
  if (!isCard) {
    isClone = whiteList.indexOf(modelName);
  }
  if (isCard || isClone !== -1) {
    return true;
  } else {
    return false;
  }
}

// 清空模型缓存
function clearModelCache() {
  modelCache = {};
}

// 世界坐标转屏幕坐标
function worldToScreen(
  camera: THREE.PerspectiveCamera,
  worldPos: THREE.Vector3,
  width: number,
  height: number
) {
  const widthHalf = width / 2;
  const heightHalf = height / 2;

  const screenPos = worldPos.clone().project(camera);
  const x = screenPos.x * widthHalf + widthHalf;
  const y = -(screenPos.y * heightHalf) + heightHalf;
  return new THREE.Vector2(x, y);
}

// 位置过渡
function positionTransition(
  from: THREE.Vector3,
  to: THREE.Vector3
): Promise<void> {
  return new Promise<void>((resolve, reject) => {
    gsap.to(from, {
      x: to.x,
      y: to.y,
      z: to.z,
      duration: 0.5,
      onComplete: () => {
        resolve();
      },
    });
  });
}

// 视角飞行
function flyToCurrentPoint(
  cameraControls: OrbitControls,
  currentPosition: THREE.Vector3,
  targetPosition: THREE.Euler
) {
  gsap.to(cameraControls.target, {
    x: currentPosition.x,
    y: 2,
    z: currentPosition.z,
    duration: 0.5,
    onComplete: () => {
      // 设置相机位置
      const distance = 1.5; // 设置相机与模型的距离
      const newX = currentPosition.x + distance * Math.sin(targetPosition.y);
      const newZ = currentPosition.z + distance * Math.cos(targetPosition.y);
      gsap.to(cameraControls.object.position, {
        x: newX,
        y: currentPosition.y + 2,
        z: newZ,
        duration: 0.5,
      });
    },
  });
}

function flyToCurrentCard(
  cameraControls: OrbitControls,
  currentPosition: THREE.Vector3
) {
  // 设置相机的目标位置
  gsap.to(cameraControls.object.position, {
    x: currentPosition.x,
    y: currentPosition.y + 0.2,
    z: currentPosition.z + 0.7,
    duration: 0.5,
  });
  gsap.to(cameraControls.target, {
    x: currentPosition.x,
    y: currentPosition.y + 0.2,
    z: currentPosition.z,
    duration: 0.5,
  });
}

// 获取当前相机坐标与视角朝向
function getCameraPositionAndDirection(orbitControls: OrbitControls) {
  const position = orbitControls.object.position.clone(); // 获取相机位置
  const vector = orbitControls.target;
  return {
    position: position,
    direction: vector,
  };
}

// 模型风格
function scienceAndTechnologyStyle1(model: THREE.Object3D, color: any = "") {
  if (model) {
    model.traverse((node) => {
      if (node instanceof THREE.Mesh) {
        const material = node.material as THREE.MeshBasicMaterial;
        material.opacity = 0.6;
        material.transparent = true;
        if (color !== "") {
          material.color.set(color);
        }
        material.map = null;
      }
    });
  }
}

function scienceAndTechnologyStyle(
  model: THREE.Object3D,
  edgeColor: number,
  faceColor: number,
  Object3D?: THREE.Object3D
) {
  // 边缘高亮
  if (model) {
    // 半透明材质
    const tpMaterial = new THREE.MeshBasicMaterial({
      transparent: true,
      opacity: 0.1,
      color: faceColor,
      depthWrite: false,
    });
    // 边缘材质
    const edgeMaterial = new THREE.MeshBasicMaterial({
      color: edgeColor,
    });
    model.traverse((node) => {
      if (node instanceof THREE.Mesh) {
        // 先修改为半透明
        node.material = tpMaterial;
        // 再添加边缘
        const geometry = new THREE.EdgesGeometry(node.geometry);
        const edges = new THREE.LineSegments(geometry, edgeMaterial);
        copyTransform(node, edges);
        if (Object3D) {
          Object3D.add(edges);
        } else {
          node.parent?.add(edges);
        }
      }
    });
  }
}

// 0-255 的 RGB 颜色值转为 Hex
function rgbToHex(r: number, g: number, b: number) {
  return r * 256 * 256 + g * 256 + b;
}
// 应用物体的文字状态设置，没有时自动创建
function applyModelTextState(
  modelObject: THREE.Object3D | undefined,
  stateAspects: StateAspect[],
  defaultStateSettings: StateAspectSetting[]
) {
  if (modelObject == null) return;

  let textGroup = modelObject.children.find((child) => {
    const userData = child.userData as SceneNodeData;
    return userData.isTextGroup;
  });

  // 如果 TextGroup 不存在，自动创建
  if (textGroup == null) {
    textGroup = new THREE.Group();
    textGroup.name = "TextGroup";
    const userData = textGroup.userData as SceneNodeData;
    userData.isTextGroup = true;
    modelObject.add(textGroup);
  }

  // 同步文字状态，如果文字对象不存在则自动创建
  // 根据 text.userData.textObjectStateAspectName 来判断对应关系
  stateAspects.forEach((stateAspect, aspectIndex) => {
    const aspectIsText = checkAspectText(stateAspect);
    if (aspectIsText) {
      const stateSetting = defaultStateSettings[aspectIndex];
      // 寻找文字对象
      const textObject = textGroup!.children.find((textObject) => {
        const userData = textObject.userData as SceneNodeData;
        return userData.textObjectStateAspectName == stateAspect.aspectName;
      });
      // 自动创建文字对象
      if (textObject == null) {
        const newTextObject = new Text(stateSetting.stateValue, {
          fontColor: rgbToHex(...stateAspect.textStateSetting!.fontColor),
          fontSize: stateAspect.textStateSetting!.fontSize,
          fontWeight: stateAspect.textStateSetting!.fontWeight,
          position: stateAspect.textStateSetting!.pivotPosition,
          textAlign: "center",
          verticalAlign: "center",
        });
        newTextObject.name = `TextObject-${stateAspect.aspectName}`;
        const userData = newTextObject.userData as SceneNodeData;
        userData.textObjectStateAspectName = stateAspect.aspectName;
        textGroup!.add(newTextObject);
      } else {
        // 同步文字状态（仅文字内容）
        (textObject as Text).setText(stateSetting.stateValue);
      }
    }
  });
}
// 根据物体名称查找网格
function findMeshByName(
  container: THREE.Object3D,
  targetName: string
): THREE.Mesh | undefined {
  let foundObject: THREE.Mesh | undefined = undefined;
  container.traverse((_node) => {
    const node = _node as THREE.Mesh;
    if (node.isMesh && node.name == targetName && foundObject == undefined)
      foundObject = node;
  });
  return foundObject;
}

// 应用模型部件设置
function applyModelPartSetting(
  object: THREE.Object3D | undefined,
  partName: string,
  setting: PartSetting,
  affectingProperties: AffectingProperty[]
) {
  if (object == null) return;

  const mesh = findMeshByName(object, partName);
  if (mesh == null) {
    // console.warn(`在模型物体中找不到部件物体“${partName}”`);
  } else {
    // 颜色（材质）
    if (affectingProperties.includes("COLOR")) {
      const material = mesh.material as THREE.MeshStandardMaterial;
      if (material == null) {
        console.warn(`找到的部件物体没有材质，无法修改颜色“${partName}”`);
      } else {
        const r = setting.color[0] / 255;
        const g = setting.color[1] / 255;
        const b = setting.color[2] / 255;
        material.color.setRGB(r, g, b);
        material.color.convertSRGBToLinear();
      }
    }
    // 位置
    if (affectingProperties.includes("POSITION")) {
      const px = setting.position[0];
      const py = setting.position[1];
      const pz = setting.position[2];
      gsap.to(mesh.position, { x: px, y: py, z: pz });
    }
    // 旋转
    if (affectingProperties.includes("ROTATION")) {
      const rx = THREE.MathUtils.degToRad(setting.rotation[0]);
      const ry = THREE.MathUtils.degToRad(setting.rotation[1]);
      const rz = THREE.MathUtils.degToRad(setting.rotation[2]);
      gsap.to(mesh.rotation, { x: rx, y: ry, z: rz });
    }
    // 显隐
    if (affectingProperties.includes("VISIBLE")) {
      mesh.visible = setting.visible;
    }
  }
}

// 应用属性的状态设置
function applyNormalDefaultStateSetting(
  object: THREE.Object3D | undefined,
  stateAspects: StateAspect[],
  stateSettings: StateAspectSetting[]
) {
  if (object == null) return;

  if (stateAspects.length > 0 && stateAspects.length != stateSettings.length) {
    console.error(
      "[applyNormalDefaultStateSetting] stateAspects 与 defaultStateSettings 没对齐"
    );
    return;
  }

  stateAspects.forEach((aspect, aspectIndex) => {
    const stateValues = aspect.stateValues;
    const aspectStateSetting = stateSettings[aspectIndex];
    const aspectIsText = checkAspectText(aspect);
    if (!aspectIsText) {
      // 应用普通状态
      stateValues.forEach((stateValue) => {
        if (stateValue.name == aspectStateSetting.stateValue) {
          stateValue?.partSettings.forEach((part) => {
            applyModelPartSetting(
              object,
              part.targetPartName,
              part,
              aspect.affectingProperties
            );
          });
        }
      });
    } else {
      // 应用文字状态
      applyModelTextState(object, stateAspects, stateSettings);
    }
  });
}

// 复制变换
function copyTransform(from: THREE.Object3D, to: THREE.Object3D) {
  to.position.copy(from.position);
  to.rotation.copy(from.rotation);
  to.scale.copy(from.scale);
}

// 读取设备状态-----------------------------------------------------------------
async function readDeviceStatus(name: string) {
  if (modelStatusCache[name] != null) {
    return modelStatusCache[name];
  } else {
    try {
      const resp = await fetch("/assets/three/models/" + name + ".json");
      const data = await resp?.json();
      if (data.stateAspects?.length > 0) {
        modelStatusCache[name] = data;
        return data;
      }
    } catch (err) {
      return null;
    }
  }
}

// 板卡弹出动画-------------------------------------------------------------------
function popUpAnimation(
  currentPosition: THREE.Vector3,
  wireFramePosition?: THREE.Vector3,
  optionalPosition?: THREE.Vector3
) {
  gsap.to(currentPosition, {
    x: currentPosition.x,
    y: currentPosition.y,
    z: currentPosition.z + 0.2,
    duration: 0.5,
  });
  if (wireFramePosition) {
    gsap.to(wireFramePosition, {
      x: wireFramePosition.x,
      y: wireFramePosition.y,
      z: wireFramePosition.z + 0.2,
      duration: 0.5,
    });
  }
  if (optionalPosition) {
    gsap.to(optionalPosition, {
      x: optionalPosition.x,
      y: optionalPosition.y,
      z: optionalPosition.z + 0.2,
      duration: 0.5,
    });
  }
}

// 保留2位小数
function r2(n: number) {
  return Math.round(n * 100) / 100;
}

//id转name
function idToName(id: string, data: { id: string; name: string }[]): string {
  const result = data.find((item) => item.id === id);
  if (result) {
    return result.name;
  } else {
    return "";
  }
}

// 欧拉角转为YPR旋转角
function eulerToYawPitchRoll(euler: THREE.Euler): [number, number, number] {
  euler = euler.clone().reorder("YXZ");

  const yaw = diff0_2PI(0, -euler.y);
  const pitch = diffHPI_HPI(0, euler.x - Math.PI);
  const roll = diff0_2PI(0, -euler.z); // 正负不确定翻了没有
  return [yaw, pitch, roll];
}

// 方向/法线转为YPR旋转角
function directionToYawPitch(direction: THREE.Vector3): [number, number] {
  const yaw = Math.atan2(direction.z, direction.x) + Math.PI / 2;
  const pitch = Math.atan2(direction.z, direction.y) + Math.PI / 2;
  return [yaw, pitch];
}

// 两角的差值，返回值在 [0, 2PI] 之间
function diff0_2PI(angle1: number, angle2: number) {
  let delta = angle2 - angle1;
  while (delta > Math.PI * 2) delta -= Math.PI * 2;
  while (delta < 0) delta += Math.PI * 2;
  return delta;
}

// 两角的差值，返回值在 [-90, 90] 之间
function diffHPI_HPI(angle1: number, angle2: number) {
  let delta = angle2 - angle1;
  while (delta > Math.PI / 2) delta -= Math.PI;
  while (delta < -Math.PI / 2) delta += Math.PI;
  return delta;
}

// 应用四元数旋转
function applyQuaternionRotation(
  model: THREE.Object3D,
  cameraQuaternion: THREE.Quaternion,
  time: number
) {
  const from = { t: 0, quaternion: new THREE.Quaternion() };
  const to = { t: 1, quaternion: new THREE.Quaternion() };
  from.quaternion.setFromEuler(model.rotation);
  to.quaternion.copy(cameraQuaternion);
  const currentQuaternion = new THREE.Quaternion();
  gsap.to(from, {
    t: to.t,
    duration: time,
    onUpdate() {
      currentQuaternion.slerpQuaternions(
        from.quaternion,
        to.quaternion,
        from.t
      );
      // from.quaternion.copy(currentQuaternion);
      model.rotation.setFromQuaternion(currentQuaternion);
      // 设置蒙版朝向
    },
  });
}
// 正面剔除
function positiveCulling(model: THREE.Object3D) {
  // side: THREE.FrontSide, // 只渲染正面
  // side: THREE.BackSide // 只渲染背面
  model.traverse((node) => {
    if (node instanceof THREE.Mesh) {
      node.material.side = THREE.FrontSide;
    }
  });
}
function addSceneLight(scene: THREE.Scene, dir: number = 1, amb: number = 0.3) {
  // 半球光源
  const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
  hemiLight.position.set(0, 300, 0); // 平行光

  const dirLight = new THREE.DirectionalLight(0xffffff, dir);
  dirLight.position.set(20, 50, 10);
  dirLight.castShadow = true; // 产生阴影 // 环境光
  const ambLight = new THREE.AmbientLight(0xffffff, amb);
  scene.add(hemiLight);
  scene.add(dirLight);
  scene.add(ambLight);
}

// 相机飞行到
async function flyCameraTo(
  camera: THREE.Camera,
  x: number,
  y: number,
  z: number,
  rx: number,
  ry: number,
  duration = 1,
  ease = "sine.inOut"
) {
  return new Promise((resolve) => {
    setTimeout(resolve, duration * 1000);
    const gsapOptions = {
      duration,
      ease,
    };
    gsap.to(camera.rotation, {
      x: rx,
      y: ry,
      ...gsapOptions,
    });
    gsap.to(camera.position, {
      x: x,
      y: y,
      z: z,
      ...gsapOptions,
    });
  });
}

// OrbitControls 的目标飞行到
async function flyControlsTargetTo(
  controls: OrbitControls,
  x: number,
  y: number,
  z: number,
  duration = 1,
  ease: string = "sine.inOut"
) {
  if (controls == null) return;

  return new Promise((resolve) => {
    setTimeout(resolve, duration * 1000);
    const gsapOptions = {
      duration,
      ease: ease,
    };
    gsap.to((controls as OrbitControls).target, {
      x: x,
      y: y,
      z: z,
      ...gsapOptions,
    });
  });
}

// 飞行相机到物体正前方一定距离处
// [x] 考虑物体有变换
// [x] 考虑物体是子节点
// [x] 考虑物体包围盒偏移
// 注意：为目标物体添加子物体，会扩展其包围盒，而导致位置不对
function flyCameraToFront(
  camera: THREE.Camera,
  object: THREE.Object3D,
  distance: number = 4,
  controlFlag: boolean = false,
  controls: any = null
) {
  // 物体包围盒计算
  boundingCalculator.calculateWithoutTransform(object);
  const objectOriginCenterOffset = boundingCalculator.center;
  // 物体位置
  const objectPosition = object.position.clone().add(objectOriginCenterOffset);
  // 考虑层级，转为世界坐标
  if (object.parent) objectPosition.applyMatrix4(object.parent.matrixWorld);
  // 物体朝向（向量）
  const objectOrient = new THREE.Vector3(0, 0, 1);
  object.getWorldDirection(objectOrient);
  const theta = Math.atan2(objectOrient.x, objectOrient.z);

  // 物体前某点位置
  objectOrient.setLength(distance);
  const objectFrontPoint = objectPosition.clone().add(objectOrient);
  if (controlFlag) {
    flyControlsTargetTo(
      controls,
      objectPosition.x,
      objectPosition.y,
      objectPosition.z
    );
  } else {
    flyCameraTo(
      camera,
      objectFrontPoint.x,
      objectFrontPoint.y,
      objectFrontPoint.z,
      0,
      theta
    );
  }
}

const whiteList = ["机柜", "BanKa"];
// 判断模型类型
function judgeModelType(model: THREE.Object3D) {
  const name = model.userData["name"].split("_");

  if (whiteList.includes(name[0])) {
    return true;
  } else {
    return null;
  }
}

async function scenePreloading() {
  console.log("执行了预加载");
  let arr = ["车载场景", "杜花路"];
  const _list = await fetch("/assets/three/models/_list.json"); // 获取id对应表
  modelNameArray = await _list.json();
  arr.forEach(async (item) => {
    try {
      const resp = await fetch(
        "/assets/three/scenes/全景车站v1/" + item + ".json",
        {
          method: "GET",
        }
      );
      const data: any = await resp.json();
      const stationGroup = new THREE.Group();
      for await (const model of data.models) {
        let modelName;
        if (model.modelId == "GROUP") {
          modelName = "GROUP";
        } else {
          modelName = idToName(model.modelId, modelNameArray);
        }

        if (!modelName) {
          continue; // modelName不存在，跳过当前循环。
        }

        const nodeLevel1: any = await loadModel2(
          modelName,
          model.code,
          stationGroup,
          model.position,
          model.rotation,
          model.scale,
          model.visible
        );
        if (model.children.length > 0) {
          for (const childModel of model.children) {
            let childName;
            if (childModel.modelId == "GROUP") {
              childName = "GROUP";
            } else {
              childName = idToName(childModel.modelId, modelNameArray);
            }

            if (!childName) {
              continue;
            }
            const childrenModel1 = loadModel2(
              childName,
              childModel.code,
              nodeLevel1,
              childModel.position,
              childModel.rotation,
              childModel.scale,
              childModel.visible
            );
          }
        }
      }
    } catch (error) {
      console.error(error);
    }
  });
}

export {
  idToName,
  loadModel2,
  findCardModel,
  worldToScreen,
  isObjectOnDoor,
  popUpAnimation,
  alarmBoardLabel,
  findGroupByName,
  readDeviceStatus,
  flyToCurrentCard,
  findModelRootNode,
  highLightTheModel,
  flyToCurrentPoint,
  createSurroundWireFrame,
  getCameraPositionAndDirection,
  findEquipmentCabinetName,
  scienceAndTechnologyStyle,
  restoreOriginalColors,
  alarmBoardLabel1,
  cloneObject,
  clearModelCache,
  eulerToYawPitchRoll,
  directionToYawPitch,
  addSceneLight,
  scienceAndTechnologyStyle1,
  applyQuaternionRotation,
  positionTransition,
  applyModelTextState,
  applyNormalDefaultStateSetting,
  positiveCulling,
  modelFlicker,
  returnLoadModel,
  flyCameraToFront,
  instantiationLoad,
  judgeModelType,
  modelCache,
  scenePreloading,
};
